﻿/**
 子页面
 ChildPage(parentid, childid)
 1.可调用父页面的服务
 2.可被父页面的事件通知到
 3.可向父页面提供服务,父页面以childs[childid].method 来调用
 */

//和扫描主对象通讯的模块
function ChildPage(parentid, childid) {
    var me = this;
    //服务，可被父页面调用
    this.serviceMethods = {};
    //父页面的通知
    this.parentEvents = {};
    //发起调用时，如有参数为方法的，保存回调
    this.invokerCallback = {};

    window.addEventListener("message", function (obj) {
        // debugger
        if (obj.data) {
            if ((obj.data.target == childid || obj.data.target == '*') && obj.data.from == 'parentpage' && obj.data.to == 'childpage' && obj.data.action) {
                var action = obj.data.action;
                if (action) {
                    var args = [];
                    if (obj.data.args) {
                        for (var i = 0; i < obj.data.args.length; i++) {
                            if ('string' == typeof obj.data.args[i] && obj.data.args[i].startsWith('/function/stub/')) {
                                //如果子页面调用父页面时，有参数是回调方法的，那么在父页面同样生成类似的回调方法
                                args.push(me.makeCallbackStub4Invoked(obj.data, obj.data.args[i]));
                            } else {
                                args.push(obj.data.args[i]);
                            }
                        }
                    }
                    if (obj.data.type == 'callback') {
                        //调用的回调
                        if (me.invokerCallback[action] && me.invokerCallback[action][obj.data.stubname] && typeof me.invokerCallback[action][obj.data.stubname] == 'function') {
                            me.invokerCallback[action][obj.data.stubname].apply(me, args);
                            obj.preventDefault();
                        }
                    } else if (obj.data.type == 'event') {
                        if (typeof me.parentEvents[action] == 'function') {
                            me.parentEvents[action].apply(me, args);
                            obj.preventDefault();
                        }
                    } else if (obj.data.type == 'service') {
                        if ("function" == typeof me.serviceMethods[action]) {
                            me.serviceMethods[action].apply(me, args);
                            obj.preventDefault();
                        }
                    }
                }
            }
        }
    });

    this.parentdata = null;

    //被子页面调用的时候用方法来保持回调存根,方法回调以 /function/stub/方法名/出线位序
    ChildPage.prototype.makeCallbackStub4Invoked = function (oridata, stubname) {
        //来源的data,方法名
        function callback() {
            var data = me.makeParamData(oridata.action, 'callback', parentid, arguments);
            //回调时，要补充一个stubname
            data.stubname = stubname;
            window.parent.postMessage(data, me.parentdata.fromdomain);
        }

        return callback;
    }

    //为调用者提供回调存根
    ChildPage.prototype.makeCallbackStub4Invoker = function (action, func, idx) {
        var stubname = '/function/stub/' + action + '/' + idx;
        this.invokerCallback[action] = this.invokerCallback[action] || {};
        this.invokerCallback[action][stubname] = func;
        return stubname;
    }

    ChildPage.prototype.makeParamData = function (action, type, target, oriargs) {
        var args = [];
        for (var i = 0; i < oriargs.length; i++) {
            if (typeof oriargs[i] == 'function') {
                args.push(this.makeCallbackStub4Invoker(action, oriargs[i], i));
            } else
                args.push(oriargs[i]);
        }
        var data = {
            action: action,
            type: type,
            target: target,
            childid: childid,
            from: 'childpage',
            fromdomain: window.location.href,
            to: 'parentpage',
            args: args
        };
        return data;
    }


    ChildPage.prototype.makeServiceMethod = function (methodName, parentdata) {
        return function () {
            // 向父页面发送方法调用请求
            var data = me.makeParamData(methodName, 'service', parentid, arguments);
            window.parent.postMessage(data, parentdata.fromdomain);
        }
    }

    ChildPage.prototype.makeEventMethod = function (methodName, parentdata) {
        return function (listener) {
            // 父页面事件通知
            this.parentEvents[methodName] = listener;
        }
    }

    function initParentServices() {
        //action, type, target, oriargs
        var data = me.makeParamData('listParentServices', 'service', parentid, [{
            childid: childid,
            fromdomain: window.location.href
        }, function (parentdata, arr) {
            if (arr) {
                for (var method of arr) {
                    me[method] = me.makeServiceMethod(method, parentdata);
                }
            }
        }]);
        window.parent.postMessage(data, '*');
    }

    initParentServices();

    function initParentEvents() {
        var data = me.makeParamData('listParentEvents', 'service', parentid, [{
            childid: childid,
            fromdomain: window.location.href
        }, function (parentdata, arr) {
            if (arr) {
                for (var method of arr) {
                    me[method] = me.makeEventMethod(method, parentdata);
                }
            }
            this.parentdata = parentdata;
            //标志初始化完成
            readycallback && readycallback(parentid);
        }]);
        window.parent.postMessage(data, '*');
    }

    initParentEvents();

    var readycallback = null;

    me.ready = function (callback) {
        readycallback = callback;
    };

    //调用父页面的方法，注册子页面可以调用的方法
    ChildPage.prototype.registerChildService = function (methodName, callback) {
        this.serviceMethods[methodName] = callback;
        var data = me.makeParamData('ChildPagePublishService', 'service', parentid, [{
            childid: childid,
            fromdomain: window.location.href
        }, methodName]);
        window.parent.postMessage(data, '*');
    };

    return me;
}